#ifndef _NETWORK_SYSTEM_H_
#define _NETWORK_SYSTEM_H_

#include <netinet/in.h>
#include <string>
#include <set>
#include <vector>

#include "GamePacket.h"
#include "../commands/GameCommand.h"
#include "../../tools/network/SelectLoop.h"

using namespace std;

class GameNetwork
{
	bool run;
	struct Node
	{
		struct sockaddr_in addr;		// In network byte order
	
		unsigned long last_seq_num;		// The highest seq_num we've heard from this peer
		unsigned long last_ok_time;		// The game time (in ms) up to which we've heard all commands from this peer
	
		Node(struct sockaddr_in addr, unsigned long initial_ok_timestamp) :
			addr(addr), last_seq_num(0), last_ok_time(initial_ok_timestamp) { }
	};
	
	/** Management of command buffer flush */
	unsigned long network_frame_num;		// Seq num associated with our frame (n'th batch of commands)
	unsigned long last_command_flush;		// The game time (in ms) up to which we've already flushed commands
	
	GamePacket::Update current_update;		// The update we're broadcasting to our peers in this network frame
	set <Node *> pending_acks;				// Peers who haven't ack'd our last update
	
	void begin_next_frame();				// Flushes command buffer
	
	/** Network state */
	SelectLoop loop;
	int socket_handler, wakeup_handler;		// Identifiers for our socket and wakeup handlers in SelectLoop
	
	pthread_t network_thread;
	static void* network_thread_main(void* data);
	void main_loop();
	
	int sock;						// Our window to the world
	vector<Node *> neighbors;		// Peers in this game (doesn't include ourself)
	
	bool hosting;					// true if we're hosting the game, false otherwise
	
	bool init_neighbors(string file_name, unsigned int node_id, unsigned long ok_timestamp);	// Reads neighborhood config file
	Node *find_peer_by_addr(struct sockaddr *from);
	
	void update_ok_timestamp();			// Returns the minimum last_ok_time among our peers
	
public:
	GameNetwork(unsigned long ok_timestamp);
	GameNetwork(const GameNetwork&);
	GameNetwork& operator=(const GameNetwork&)	{ return *this; }
	~GameNetwork();
	
	/*	The game time (in ms) up to which we've heard all commands from all peers. Game is allowed to simulate frames that
		end before (and don't include) this time.
	*/
	unsigned long ok_timestamp;
	
	bool i_am_host();
	
	static int init_socket(int sock);	// Create and bind to UDP socket
	void init(const NetworkConf&);
};

#endif
